package View;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JViewport;

import network.protocol.PlayerPackage;
import UserInterface.PaintableInterface;
import client.gameplay.Building;
import client.gameplay.Chat;
import client.gameplay.Console;
import client.gameplay.DrawingListener;
import client.gameplay.LoadingListener;
import client.gameplay.Unit;
import client.network.ClientInterface;

@SuppressWarnings("serial")
/**
 * The view to the game. 
 */
public class View extends JComponent implements DrawingListener,
		LoadingListener {
	public static final Color PLAYER_ONE_COLOR = new Color(239, 160, 15);
	public static final Color PLAYER_TWO_COLOR = new Color(232, 97, 97);
	public static final Color PLAYER_THREE_COLOR = new Color(165, 232, 97);
	public static final Color PLAYER_FOUR_COLOR = new Color(230, 83, 193);
	public static final Color PLAYER_FIVE_COLOR = new Color(83, 230, 230);
	public static final Color PLAYER_SIX_COLOR = new Color(107, 139, 233);
	public static final Color PLAYER_SEVEN_COLOR = new Color(161, 92, 231);
	public static final Color PLAYER_EIGHT_COLOR = new Color(99, 232, 132);
	public static final int ARMORY_INDEX = 6;
	public static final int BUILD_MENU_ARMORY_INDEX = 4;
	public static final int BUILD_MENU_UPGRADE_INDEX = 1;
	public static final int BUILD_MENU_TOWER_INDEX = 2;
	public static final int BUILD_MENU_HOUSE_INDEX = 3;
	public static final int BUILD_MENU_UNAVAILABLE_INDEX = 0;
	public static final int DUST_CLOUND_INDEX = 2;
	public static final int MAX_PLAYERS = 9;
	public static final int MORALE_SUN_ACTIVE_INDEX = 3;
	public static final int MORALE_SUN_INACTIVE_INDEX = 4;
	public static final int NUMBER_OF_DIFFERENT_BUILDINGS = 7;
	public static final int NUMBER_OF_UNIT_IMAGES = 8;
	public static final int NUMBER_OF_BUILD_MENU_OPTIONS = 5;
	public static final int POPULATION_CLOUND_INDEX = 0;
	public static final int SCAFFOLD_INDEX = 1;
	public static final int TOWER_INDEX = 5;

	private static final Color CONSOLE_BACKGROUNDCOLOR = new Color(0, 0, 0, 150);
	private static final Color CONSOLE_SERVER_COLOR = new Color(176, 226, 255);
	private static final Color TOWER_OVAL_COLOR = new Color(255, 107, 35, 127);
	private static final float ARROW_WIDTH = 10;
	private static final float TOTALPOPULATIONBAR_HEIGHT = 40;
	private static final Font WINNER_FONT = new Font("arial", Font.BOLD, 150);
	private static final Font STD_FONT = new Font(null);
	private static final int UNIT_DISPLACEMENT_X = -10;
	private static final int UNIT_DISPLACEMENT_Y = 0;
	private static final int POPULATION_STRING_DISPLACEMENT_Y = 15;
	private static final int POPULATION_STRING_DISPLACEMENT_X = 5;
	private static final int MARKER_DIAMETER_X = 130;
	private static final int MARKER_DIAMETER_Y = 70;
	private static final int MARKER_XPOS = -65;
	private static final int MARKER_YPOS = -30;
	private static final int MARKER_WIDTH = 10;
	private static final int BACK_SCAFFOLD_X = -20;
	private static final int BACK_SCAFFOLD_Y = -65;
	private static final int FRONT_SCAFFOLD_X = -75;
	private static final int FRONT_SCAFFOLD_Y = -30;
	private static final int BUILDING_DISPLACEMENT_X = -44;
	private static final int BUILDING_DISPLACEMENT_Y = -90;
	private static final int DUST_DISPLACEMENT_X = -60;
	private static final int DUST_DISPLACEMENT_Y = -30;
	private static final int CLOUD_DISPLACEMENT_X = -75;
	private static final int CLOUD_DISPLACEMENT_Y = -80;
	private static final int POPULATION_DISPLACEMENT_X = -60;
	private static final int POPULATION_DISPLACEMENT_Y = -60;
	private static final int RIGHT_CORNER_INDEX = 3;
	private static final int CHAT_HEIGHT_DISPLACEMENT = -20;
	private static final String path = System.getProperty("user.dir")
			+ "\\textures\\";
	private static final Font CHAT_FONT = new Font("Arial", Font.ITALIC, 18);

	private ArrayList<BackgroundObject> backgroundTextures;
	private ArrayList<BufferedImage> miscImg;
	private BufferedImage[] buildingMenuItems;
	private BufferedImage[][] buildingImgs;
	private BufferedImage[][] unitImgs;
	private BuildDisplay buildingDisplay;
	private BuildMenu buildMenu;
	private ClientInterface gamePlay;
	private JViewport viewPort;
	private Map<Integer, Color> playerColor;
	private MoraleDisplay moraleDisplay;
	private TotalPopulationDisplay totalPopulationDisplay;
	private UnitDisplay unitDisplay;

	private static boolean viewLoaded = false;

	public View(ClientInterface gamePlay, JViewport vp) {
		// All related to the init of view
		viewPort = vp;
		this.gamePlay = gamePlay;
		gamePlay.addDrawingListener(this);
		this.setOpaque(false);

		// initialize standard features (images, fields containing objects
		// keeping track of representation of the model)
		initFields();
		loadImages();

	}

	private void initFields() {
		buildingMenuItems = new BufferedImage[NUMBER_OF_BUILD_MENU_OPTIONS];
		buildingImgs = new BufferedImage[MAX_PLAYERS][NUMBER_OF_DIFFERENT_BUILDINGS];
		unitImgs = new BufferedImage[MAX_PLAYERS][NUMBER_OF_UNIT_IMAGES];
		playerColor = new HashMap<Integer, Color>();
		miscImg = new ArrayList<BufferedImage>();
		backgroundTextures = new ArrayList<BackgroundObject>();

	}

	private void loadImages() {

		prepBuildMenuImg();
		prepBuildingsImage();
		prepMiscImages();
		prepUnitImage();
	}

	private void initDisplay() {
		unitDisplay = new UnitDisplay(gamePlay.getPlayers(), unitImgs);
		buildingDisplay = new BuildDisplay(gamePlay.getPlayers(), buildingImgs,
				miscImg);
		buildMenu = new BuildMenu(buildingMenuItems, gamePlay.getCalcArrow());
		moraleDisplay = new MoraleDisplay(viewPort,
				miscImg.get(MORALE_SUN_ACTIVE_INDEX),
				miscImg.get(MORALE_SUN_INACTIVE_INDEX), gamePlay.getPlayers());
		totalPopulationDisplay = new TotalPopulationDisplay(
				gamePlay.getPlayers(), viewPort);
	}

	private void setColor() {
		Color[] color = new Color[MAX_PLAYERS];
		color[0] = PLAYER_ONE_COLOR;
		color[1] = PLAYER_TWO_COLOR;
		color[2] = PLAYER_THREE_COLOR;
		color[3] = PLAYER_FOUR_COLOR;
		color[4] = PLAYER_FIVE_COLOR;
		color[5] = PLAYER_SIX_COLOR;
		color[6] = PLAYER_SEVEN_COLOR;
		color[7] = PLAYER_EIGHT_COLOR;

		for (int i = 1; i < gamePlay.getPlayers().size(); i++) {
			playerColor.put(gamePlay.getPlayers().get(i).getID(), color[i - 1]);

		}
	}

	private void prepMiscImages() {
		miscImg.add(getBufferedImage(new File(path + "interface\\residents.png")));
		miscImg.add(getBufferedImage(new File(path + "interface\\upgrade.png")));
		miscImg.add(getBufferedImage(new File(path + "interface\\convert.png")));
		miscImg.add(getBufferedImage(new File(path + "interface\\moralesun.png")));
		miscImg.add(getBufferedImage(new File(path
				+ "interface\\moralesundown.png")));
	}

	private void prepBuildMenuImg() {
		buildingMenuItems[BUILD_MENU_UNAVAILABLE_INDEX] = getBufferedImage(new File(
				path + "interface\\menuUnavailable.png"));
		buildingMenuItems[BUILD_MENU_UPGRADE_INDEX] = getBufferedImage(new File(
				path + "interface\\menuUpgrade.png"));
		buildingMenuItems[BUILD_MENU_TOWER_INDEX] = getBufferedImage(new File(
				path + "interface\\menuTower.png"));
		buildingMenuItems[BUILD_MENU_HOUSE_INDEX] = getBufferedImage(new File(
				path + "interface\\menuHouse.png"));
		buildingMenuItems[BUILD_MENU_ARMORY_INDEX] = getBufferedImage(new File(
				path + "interface\\menuArmory.png"));
	}

	private void prepBuildingsImage() {
		for (int n = 0; n < MAX_PLAYERS; n++) {
			buildingImgs[n][0] = getBufferedImage(new File(path
					+ "buildings\\houselvl0" + n + ".png"));
			buildingImgs[n][1] = getBufferedImage(new File(path
					+ "buildings\\houselvl1" + n + ".png"));
			buildingImgs[n][2] = getBufferedImage(new File(path
					+ "buildings\\houselvl2" + n + ".png"));
			buildingImgs[n][3] = getBufferedImage(new File(path
					+ "buildings\\houselvl3" + n + ".png"));
			buildingImgs[n][4] = getBufferedImage(new File(path
					+ "buildings\\houselvl4" + n + ".png"));
			buildingImgs[n][5] = getBufferedImage(new File(path
					+ "buildings\\tower" + n + ".png"));
			buildingImgs[n][6] = getBufferedImage(new File(path
					+ "buildings\\armory" + n + ".png"));
		}
	}

	private void prepUnitImage() {
		for (int n = 1; n < MAX_PLAYERS; n++) {
			for (int i = 0; i < NUMBER_OF_UNIT_IMAGES; i++) {
				unitImgs[n][i] = getBufferedImage(new File(path + "unit\\unit"
						+ n + i + ".png"));
			}
		}
	}

	private BufferedImage getBufferedImage(File file) {
		BufferedImage img = null;
		try {
			img = ImageIO.read(file);
		} catch (IOException e) {
			System.out.println("Failed at reading ifmagefile.");
			System.out.println("url: " + file.toString());
		}
		return img;
	}

	public void setDim(Dimension d) {
		this.setPreferredSize(d);
		this.setSize(d);
	}

	public void paintComponent(final Graphics g) {
		Graphics2D g2 = (Graphics2D) g;

		// Hints for better speed.
		// *
		g2.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION,
				RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED);
		g2.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,
				RenderingHints.VALUE_COLOR_RENDER_SPEED);
		g2.setRenderingHint(RenderingHints.KEY_DITHERING,
				RenderingHints.VALUE_DITHER_DISABLE);
		g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
				RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
		g2.setRenderingHint(RenderingHints.KEY_RENDERING,
				RenderingHints.VALUE_RENDER_SPEED);
		// */

		// check if the game shall be displayed
		if (gamePlay.getDisplayGame() && viewLoaded) {
			// Background texture!
			Iterator<BackgroundObject> itr = backgroundTextures.iterator();
			BackgroundObject backgroundObject;
			while (itr.hasNext()) {
				backgroundObject = itr.next();
				g2.drawImage(backgroundObject.getTexture(),
						backgroundObject.getPosition().x,
						backgroundObject.getPosition().y, null);
			}

			// Draw Marker
			g2.setColor(Color.YELLOW);
			g2.setStroke(new BasicStroke(MARKER_WIDTH));
			g2.drawOval(
					gamePlay.getUserInterface()
							.get(PaintableInterface.MARKER_INDEX).getStart().x
							+ MARKER_XPOS,
					gamePlay.getUserInterface()
							.get(PaintableInterface.MARKER_INDEX).getStart().y
							+ MARKER_YPOS, MARKER_DIAMETER_X, MARKER_DIAMETER_Y);

			// Draw Tower Oval
			g2.setColor(TOWER_OVAL_COLOR);
			Iterator<Building> itrBuilding = gamePlay.getBuildings().iterator();
			while (itrBuilding.hasNext()) {
				Building b = itrBuilding.next();
				if (b.isTower())
					g2.drawOval(b.getPos().x - Building.TOWER_RANGE_WIDTH,
							b.getPos().y - Building.TOWER_RANGE_HEIGHT,
							Building.TOWER_RANGE_WIDTH * 2,
							Building.TOWER_RANGE_HEIGHT * 2);
			}

			// Draw Units...
			Unit u;
			for (int i = gamePlay.getUnits().size() - 1; i >= 0; i--) {
				u = gamePlay.getUnits().get(i);
				g2.drawImage(unitDisplay.gutUnitImg(u), u.getPosition().x
						+ UNIT_DISPLACEMENT_X, u.getPosition().y
						+ UNIT_DISPLACEMENT_Y, null);
			}

			// Draw buildings

			itrBuilding = gamePlay.getBuildings().iterator();
			Building b;
			while (itrBuilding.hasNext()) {
				b = itrBuilding.next();
				if (b.upgrading() > 0 || b.getConvertTime() > 0)
					g2.drawImage(buildingDisplay.getScaffold(), b.getPos().x
							+ BACK_SCAFFOLD_X, b.getPos().y + BACK_SCAFFOLD_Y,
							null);
				g2.drawImage(buildingDisplay.getImg(b), b.getPos().x
						+ BUILDING_DISPLACEMENT_X, b.getPos().y
						+ BUILDING_DISPLACEMENT_Y, null);
				if (b.upgrading() > 0 || b.getConvertTime() > 0)
					g2.drawImage(buildingDisplay.getDust(), b.getPos().x
							+ DUST_DISPLACEMENT_X, b.getPos().y
							+ DUST_DISPLACEMENT_Y, null);
				if (b.getConvertTime() > 0 || b.upgrading() > 0)
					g2.drawImage(buildingDisplay.getScaffold(), b.getPos().x
							+ FRONT_SCAFFOLD_X,
							b.getPos().y + FRONT_SCAFFOLD_Y, null);
				if (b.getOwner().equals(gamePlay.getMyself())
						|| b.getOwner().getID() == PlayerPackage.NEUTRAL_PLAYER) {
					g2.drawImage(buildingDisplay.getResCloud(), b.getPos().x
							+ CLOUD_DISPLACEMENT_X, b.getPos().y
							+ CLOUD_DISPLACEMENT_Y, null);
					g2.setColor(Color.BLACK);
					g2.drawString(Integer.toString(b.getResidents()),
							b.getPos().x + POPULATION_DISPLACEMENT_X,
							b.getPos().y + POPULATION_DISPLACEMENT_Y);
				}
			}

			// draw user interface arrow
			if (gamePlay.getUserInterface().get(PaintableInterface.ARROW_INDEX)
					.isActive()) {
				g2.setColor(Color.YELLOW);
				g2.setStroke(new BasicStroke(ARROW_WIDTH));
				g2.drawLine(
						gamePlay.getUserInterface()
								.get(PaintableInterface.ARROW_INDEX).getStart().x,
						gamePlay.getUserInterface()
								.get(PaintableInterface.ARROW_INDEX).getStart().y,
						gamePlay.getUserInterface()
								.get(PaintableInterface.ARROW_INDEX).getEnd().x,
						gamePlay.getUserInterface()
								.get(PaintableInterface.ARROW_INDEX).getEnd().y);
			}

			// Fix drawing for shots...
			// Tower shot!

			// Nothing to see here, move along...

			// Draw convert to / upgrade menu.

			if (gamePlay.isBuildMenu()) {
				buildMenu.update();
				g2.drawImage(buildMenu.getUpgradeImg(),
						buildMenu.getUpgradePos().x,
						buildMenu.getUpgradePos().y, null);
				g2.drawImage(buildMenu.getConvToTowerImg(),
						buildMenu.getConvToTowerPos().x, buildMenu
								.getConvToTowerPos().y, null);
				g2.drawImage(buildMenu.getConvToHouseImg(),
						buildMenu.getConvToHousePos().x, buildMenu
								.getConvToHousePos().y, null);
				g2.drawImage(buildMenu.getConvToArmoryImg(),
						buildMenu.getConvToArmoryPos().x, buildMenu
								.getConvToArmoryPos().y, null);
			}

			// Draw morale, set individual positions for each player
			moraleDisplay.updatePos();
			for (int i = 1; i < gamePlay.getPlayers().size(); i++) {
				for (int j = 0; j < MoraleDisplay.NUMBER_OF_MORALE_STARS; j++) {
					g2.drawImage(moraleDisplay.getImg(i, j),
							moraleDisplay.getPos(i, j).x,
							moraleDisplay.getPos(i, j).y, null);
				}
			}

			// Draw population status.
			totalPopulationDisplay.update();
			for (int i = 0; i < gamePlay.getPlayers().size() - 1; i++) {
				g2.setColor(totalPopulationDisplay.getPlayerColor(i));
				g2.fillRect(totalPopulationDisplay.getStartX(i),
						totalPopulationDisplay.y,
						(int) totalPopulationDisplay.getPlayerLength(i),
						(int) TOTALPOPULATIONBAR_HEIGHT);
				g2.setColor(Color.BLACK);
				g2.drawString(
						Integer.toString(totalPopulationDisplay.getPop(i)),
						(int) (totalPopulationDisplay.getStartX(i) + POPULATION_STRING_DISPLACEMENT_X),
						totalPopulationDisplay.y
								+ POPULATION_STRING_DISPLACEMENT_Y);
			}
			if (gamePlay.isWinningCondMet()) {
				g2.setColor(CONSOLE_BACKGROUNDCOLOR);
				g2.fillRect(viewPort.getViewPosition().x,
						viewPort.getViewPosition().y + Console.CONSOLE_HEIGHT,
						Toolkit.getDefaultToolkit().getScreenSize().width,
						Toolkit.getDefaultToolkit().getScreenSize().height);
				g2.setColor(Color.WHITE);
				g2.setFont(WINNER_FONT);
				g2.drawString(gamePlay.getWinnerNick() + " WIN!",
						viewPort.getViewPosition().x,
						viewPort.getViewPosition().y + Console.CONSOLE_HEIGHT
								+ WINNER_FONT.getSize());
				g2.setFont(STD_FONT);
			}
		}

		// The console
		if (gamePlay.getConsole().isShowingConsole()
				|| gamePlay.isWinningCondMet()) {
			int x = viewPort.getViewPosition().x;
			int y = viewPort.getViewPosition().y;

			// set console to transparent
			g2.setColor(CONSOLE_BACKGROUNDCOLOR);
			g2.fillRect(x, y,
					Toolkit.getDefaultToolkit().getScreenSize().width,
					Console.CONSOLE_HEIGHT);
			String messages = gamePlay.getConsole().getMessages();

			int start = 0, end = 0, i = 1;
			while (messages.indexOf('\n', start) != -1) {
				end = messages.indexOf('\n', start);

				// check if there is a special-message that should be painted
				// in a different color
				if (messages.substring(start, end).indexOf("[Server]: ") != -1)
					g2.setColor(CONSOLE_SERVER_COLOR);
				else if (messages.substring(start, end).indexOf("[Warning]: ") != -1)
					g2.setColor(Color.RED);
				else
					g2.setColor(Color.GREEN);

				g2.drawString(messages.substring(start, end), x
						+ Console.TEXT_INLINE, i * Console.TEXTHEIGHT + y);
				start = end + 1;
				i++;
			}
			g2.setColor(Color.GREEN);
			g2.drawString(gamePlay.getConsole().getLastCommand(), x
					+ Console.TEXT_INLINE, i * Console.TEXTHEIGHT + y);
		}
		// ==================

		// =====The chat====
		if (gamePlay.getDisplayGame() && viewLoaded) {
			g2.setFont(CHAT_FONT);
			g2.setColor(Color.BLACK);

			int chatPosX = viewPort.getViewPosition().x + Chat.TEXT_INLINE_X;
			int chatPosY = moraleDisplay.getPos(RIGHT_CORNER_INDEX, 0).y
					+ CHAT_HEIGHT_DISPLACEMENT;

			int n = 0;
			Iterator<String> itr = gamePlay.getChat().getMessages().iterator();
			while (itr.hasNext()) {
				g2.drawString(itr.next(), chatPosX, chatPosY - n
						* Chat.TEXTHEIGHT);
				n++;
			}
		}
		// =================
	}

	public void notifyDrawingChanged() {
		// Whenever something changed in the drawing,
		// we simply repaint the entire component. This will call various
		// methods in the Swing library that ensures that eventually,
		// in another thread, our own paintComponent() method will be called.
		repaint();
	}

	public void setBgColor(Color color) {
		return;
	}

	public void load() {
		// check if the map is smaller than the screen & fix a bug with that
		Dimension d = gamePlay.getDim();
		if (Toolkit.getDefaultToolkit().getScreenSize().width > d.width)
			d.width = Toolkit.getDefaultToolkit().getScreenSize().width;

		if (Toolkit.getDefaultToolkit().getScreenSize().height > d.height)
			d.height = Toolkit.getDefaultToolkit().getScreenSize().height;
		// ==
		viewPort.setExtentSize(gamePlay.getDim());
		setBackgroundTextures();
		setColor();
		initDisplay();
		viewLoaded = true;
	}

	private void setBackgroundTextures() {
		ArrayList<File> fileNames = gamePlay.getBackgroundTextureFileNames();
		for (int i = 0; i < fileNames.size(); i++) {
			backgroundTextures.add(new BackgroundObject(
					getBufferedImage(fileNames.get(i)), gamePlay
							.getBackgroundTextureFileNamePoints().get(i)));
		}
	}

}
